# -*- coding = utf-8 -*-
# @Time : 2021/8/30 17:37
# @Author : 龙王赘婿_彪少
# @File : music.py
# @Software: PyCharm

# 别人的爬虫 爬取音乐

# https://www.cnblogs.com/GadyPu/p/13560855.html

"""
===================================
    -*- coding:utf-8 -*-
    Author     :GadyPu
    E_mail     :Gadypy@gmail.com
    Time       :2020/8/25 0020 上午 12:08
    FileName   :migu_music.py
===================================
"""
import os
import requests
import warnings
from lxml import etree
from enum import Enum
from queue import Queue
import threading

warnings.filterwarnings('ignore')


class MusicType(Enum):
    Normal = 1
    HQ = 2
    SQ = 3


class MiguMusic(object):

    def __init__(self):
        self.req_url = 'https://music.migu.cn/v3/api/music/audioPlayer/getPlayInfo?'
        self.serach_api = 'https://music.migu.cn/v3/api/search/suggest?'
        self.headers = {
            'referer': 'https://music.migu.cn/v3/music/player/audio',
            'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36',
        }
        self.headers_song = {
            'Host': 'freetyst.nf.migu.cn',
            'Upgrade-Insecure-Requests': '1',
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36'
        }
        self.params = {
            'dataType': 2,
            'data': None,
            'secKey': None
        }
        self.que = Queue()
        self.song_list = []

    def get_music_download_url(self, song_id, type=3):
        '''
        获取歌曲下载链接
        :param song_id: 歌曲copyrightId
        :param type: 默认下载无损格式的音频
        :return:
        '''
        try:
            secKey = os.popen('Node fuck.js ' + f'{song_id} {type}').read().split(' ')
        except:
            print('error cannot create secKey')
            return None
        self.params['data'] = secKey[0]
        self.params['secKey'] = secKey[1]
        try:
            response = requests.get(url=self.req_url, headers=self.headers, params=self.params, verify=False)
            print(response.text)
            if response.status_code == 200:
                url = 'http:' + response.json()['data']['playUrl']
                return url
        except:
            print('network error please try again...')
        return None

    def get_singer_or_music_id(self, keyword, type):
        '''
        获取歌手id或者歌曲id
        :param keyword:
        :param type:  type == 1 keyword为歌手id，反之为歌曲id
        :return:
        '''
        param = {'keyword': keyword}
        headers = {
            'referer': 'https://music.migu.cn/v3?keyword=',
            'Host': 'music.migu.cn',
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36'
        }
        try:
            response = requests.get(url=self.serach_api, headers=headers, params=param, verify=False)
            data = response.json()
            return data['data']['singers'][0]['id'] if type == 1 else data['data']['songs'][0]['copyrightId']
        except:
            return None

    def get_song_list(self, keyword, page=1, type=1):
        '''
        获取歌手主页歌曲信息
        eg: https://music.migu.cn/v3/music/artist/559/song
        :param keyword: 歌手名称或歌曲名称
        :param page:  歌手主页，页码
        :param type:  type == 1，keyword为歌手名称、type == 2, keyword为歌曲名称
        :return:  None
        '''
        id = self.get_singer_or_music_id(keyword, type)
        if not id or type == 2:
            if id:
                self.song_list.append([id, keyword, MusicType['SQ'].value])
            return None
        songs_api = 'https://music.migu.cn/v3/music/artist/{id}/song?page={page}'
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36'
        }
        url = songs_api.format(
            id=id, page=page
        )
        try:
            # 歌手主页，获取歌曲：id、名称、音质
            response = requests.get(url=url, headers=headers, verify=False)
            html = etree.HTML(response.text)
            ids = html.xpath('//div[@class="song-name J_SongName"]/a/@href')
            titles = html.xpath('//div[@class="song-name J_SongName"]/a/@title')
            types = html.xpath('//div[@class="song-name J_SongName"]/i/text()')
            for _id, tit, typ in zip(ids, titles, types):
                try:
                    __typ = MusicType[typ.upper()].value
                except:
                    __typ = MusicType['Normal'].value
                finally:
                    self.song_list.append([_id.split('/')[-1], tit, __typ])
                # print(_id, tit, typ)
        except Exception as err:
            print(err)

    def download_thread(self, path):
        '''
        下载线程
        :param path: 下载保存的路径
        :return:
        '''
        while not self.que.empty():
            __d = self.que.get()
            try:
                resp = requests.get(url=__d[0], headers=self.headers_song, stream=True)
                if resp.status_code == 200:
                    print(f'start downloading {__d[1]}')
                    file_path = os.path.join(path, f'{__d[1]}.flac')
                    with open(file_path, 'wb') as wf:
                        for data in resp.iter_content(1024):
                            if data:
                                wf.write(data)
                    print(f'the file {__d[1]} download complished...')
            except:
                print('download network error...')

    def run(self, keyword, page=1, type=1):
        '''
        程序入口
        :param keyword: 歌手名称或歌曲名称
        :param page: 默认下载歌手主页第一页的所有歌曲
        :param type: type == 1，keyword为歌手名称、type == 2, keyword为歌曲名称
        :return:
        '''
        self.get_song_list(keyword, page, type)
        if not self.song_list:
            print('song list empty...')
            return
        for i in self.song_list:
            if i and i[0]:
                self.que.put((self.get_music_download_url(i[0], i[2]), i[1]))
        # keyword为歌手，创建歌手文件夹，否则保存路径为当前目录下的music文件夹
        path = os.path.join(os.getcwd(), keyword if type == 1 else 'music')
        if not os.path.exists(path):
            os.makedirs(path)
        thread_list = []
        for i in range(3):
            t = threading.Thread(target=self.download_thread, args=(path,))
            t.setDaemon(True)
            thread_list.append(t)
            t.start()
        [_.join() for _ in thread_list]


if __name__ == '__main__':
    d = MiguMusic()
    # d.run('许巍') #用歌手名下载
    d.run('曾经的你', 1, 2)